/**************************************************************************** 
 * Meta，一个轻量级的静态分区Hypervisor 
 *
 * 版权(c) , 2022-
 *
 * 作者:
 *      Tupelo Shen <shenwanjiang2013@163.com>
 *
 * Meta是一个自由软件，你可以在遵循GNU GPLv2协议的条件下，重新修改并发布它。
 *
 ***************************************************************************/

#include <vm.h>
#include <string.h>
#include <fences.h>
#include <arch/tlb.h>

/****************************************************************************
 ****                           私有函数接口                             ****
 ****************************************************************************/
static uint64_t vm_cpuid_to_mpidr(struct vm* vm, uint64_t cpuid);

/****************************************************************************
 ****                           公共函数定义                             ****
 ****************************************************************************/

/**
 * 功能描述: vm初始化（主要是GICD初始化）
 * 入口参数: vm,    VM数据结构
 *           config,配置信息
 * 出口参数: void
 * 返 回 值: void
 * 修改日期: 2022-09-21
 */
void vm_arch_init(struct vm* vm, const struct vm_config* config)
{
    if (vm->master == cpu.id) {
        vgic_init(vm, &config->platform.arch.gic);
    }
}

/**
 * 功能描述: 虚拟核初始化
 * 入口参数: vcpu,  虚拟核
 *           vm,    运行在虚拟核上的VM
 * 出口参数: void
 * 返 回 值: void
 * 修改日期: 2022-09-21
 */
void vcpu_arch_init(struct vcpu* vcpu, struct vm* vm)
{
    /* vmpidr寄存器 */
    vcpu->arch.vmpidr = vm_cpuid_to_mpidr(vm, vcpu->id);
    MSR(VMPIDR_EL2, vcpu->arch.vmpidr);

    /* 设置vcpu的启动状态（先启动核0）*/
    vcpu->arch.psci_ctx.state = vcpu->id == 0 ? ON : OFF;

    /* 设置VM的root页表 */
    paddr_t root_pt_pa;
    mem_translate(&cpu.as, (vaddr_t)vm->as.pt.root, &root_pt_pa);
    MSR(VTTBR_EL2, ((vm->id << VTTBR_VMID_OFF) & VTTBR_VMID_MSK) |
                       (root_pt_pa & ~VTTBR_VMID_MSK));

    /* 失效tlb表 */
    ISB();  // make sure vmid is commited befor tlbi
    tlb_vm_inv_all(vm->id);

    /* vcpu的私有中断初始化 */
    vgic_cpu_init(vcpu);
}

/**
 * 功能描述: 虚拟核复位
 * 入口参数: vcpu:  要复位的虚拟核
 *           entry: 复位后，虚拟核要运行的入口地址
 * 出口参数: void
 * 返 回 值: void
 * 修改日期: 2022-09-21
 */
void vcpu_arch_reset(struct vcpu* vcpu, vaddr_t entry)
{
    memset(vcpu->regs, 0, sizeof(struct arch_regs));

    vcpu->regs->elr_el2 = entry;
    vcpu->regs->spsr_el2 = SPSR_EL1h | SPSR_F | SPSR_I | SPSR_A | SPSR_D;

    MSR(CNTVOFF_EL2, 0);

    /**
     *  See ARMv8-A ARM section D1.9.1 for registers that must be in a known
     * state at reset.
     */
    MSR(SCTLR_EL1, SCTLR_RES1);
    MSR(CNTKCTL_EL1, 0);
    MSR(PMCR_EL0, 0);

    /**
     *  TODO: ARMv8-A ARM mentions another implementation optional registers
     * that reset to a known value.
     */
}

/**
 * 功能描述: 运行虚拟核（与架构相关的部分）
 *           如果虚拟核的处于不运行状态，则进入空闲状态
 * 入口参数: vcpu:  虚拟核
 * 出口参数: void
 * 返 回 值: void
 * 修改日期: 2022-09-21
 */
void vcpu_arch_run(struct vcpu* vcpu)
{
    // TODO: consider using TPIDR_EL2 to store vcpu pointer
    if (vcpu->arch.psci_ctx.state == ON) {
        vcpu_arch_entry(); /* 汇编实现 */
    } else {
        cpu_idle();
    }
    
}

/**
 * 功能描述: 写虚拟核的通用寄存器
 * 入口参数: vcpu:  虚拟核
 *           reg:   通用寄存器
 *           val:   写入通用寄存器的值
 * 出口参数: void
 * 返 回 值: void
 * 修改日期: 2022-09-21
 */
void vcpu_writereg(struct vcpu* vcpu, unsigned long reg, unsigned long val)
{
    if (reg > 30) return;
    vcpu->regs->x[reg] = val;
}

/**
 * 功能描述: 读虚拟核的通用寄存器
 * 入口参数: vcpu:  虚拟核
 *           reg:   通用寄存器
 * 出口参数: void
 * 返 回 值: uint64_t, 返回通用寄存器的值
 * 修改日期: 2022-09-21
 */
unsigned long vcpu_readreg(struct vcpu* vcpu, unsigned long reg)
{
    if (reg > 30) return 0;
    return vcpu->regs->x[reg];
}

/**
 * 功能描述: 通过mpidr值获取vcpu
 * 入口参数: vm,    虚拟核
 *           mpidr, 多核处理标识符寄存器
 * 出口参数: void
 * 返 回 值: struct vcpu, 虚拟核数据结构
 * 修改日期: 2022-09-21
 */
struct vcpu* vm_get_vcpu_by_mpidr(struct vm* vm, unsigned long mpidr)
{
    list_foreach(vm->vcpu_list, struct vcpu, vcpu)
    {
        if ((vcpu->arch.vmpidr & MPIDR_AFF_MSK) == (mpidr & MPIDR_AFF_MSK))  {
            return vcpu;
        }
    }

    return NULL;
}

unsigned long vcpu_readpc(struct vcpu* vcpu)
{
    return vcpu->regs->elr_el2;
}

void vcpu_writepc(struct vcpu* vcpu, unsigned long pc)
{
    vcpu->regs->elr_el2 = pc;
}

/****************************************************************************
 ****                         私有函数定义                               ****
 ****************************************************************************/
/**
 * 功能描述: 将VM的cpuid转换成mpidr（将线性编号转换成亲和力值）
 * 入口参数: vm,    VM的数据结构
 *           cpuid, VM的cpuid
 * 出口参数: void
 * 返 回 值: uint64_t, 返回mpidr值
 * 修改日期: 2022-09-21
 */
static unsigned long vm_cpuid_to_mpidr(struct vm* vm, vcpuid_t cpuid)
{
    return platform_arch_cpuid_to_mpdir(&vm->config->platform, cpuid);
}